//--------------------------------------------------------------
//--------------------------------------------------------------
package GatewayWorker

//------------------------------------------------------------------------------
import (
	"errors"
	"fmt"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
	uuid "github.com/satori/go.uuid"
)

//------------------------------------------------------------------------------
var (
	upgrade = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
)

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
type WsMessage struct {
	Type    int    // 协议
	Message []byte //消息体
}

type Nil struct{}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
type Connection struct {
	id        string
	uid       string
	wsConn    *websocket.Conn // websocket 连接句柄
	readChan  chan *WsMessage // 读消息
	writeChan chan *WsMessage // 写消息
	closeChan chan byte       // 关闭信号
	once      sync.Once       // 线程安全通知连接已关闭

	groups map[string]Nil
}

//------------------------------------------------------------------------------
func (o *Connection) Close() {
	_ = o.wsConn.Close()
	o.once.Do(func() {
		o.closeChan <- 1
		close(o.closeChan)
	})
}

//------------------------------------------------------------------------------
func (o *Connection) readMessage() (data *WsMessage, err error) {
	select {
	case data = <-o.readChan:
	case <-o.closeChan:
		fmt.Println("read")
		err = errors.New("connection is closed")
	}
	fmt.Println(data)
	return
}

//------------------------------------------------------------------------------
func (o *Connection) writeMessage(data *WsMessage) (err error) {
	select {
	case o.writeChan <- data:
	case <-o.closeChan:
		fmt.Println("write")
		err = errors.New("connection is closed")
	}

	return
}

//------------------------------------------------------------------------------
func (o *Connection) readLoop() {
	for {
		messageType, data, err := o.wsConn.ReadMessage()
		if err != nil {
			fmt.Println(err)
			goto CLOSE
		}
		fmt.Println(data)
		o.readChan <- &WsMessage{Type: messageType, Message: data}
	}

CLOSE:
	o.Close()
}

//------------------------------------------------------------------------------
func (o *Connection) writeLoop() {
	for {
		select {
		case data := <-o.writeChan:
			err := o.wsConn.WriteMessage(data.Type, data.Message)
			if err != nil {
				fmt.Println("write fail")
				fmt.Println(err)
				goto ERROR
			}

		case <-o.closeChan:
			fmt.Println("writeLoop fail")
			goto ERROR
		}

	}
ERROR:
	o.Close()
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
type userInfo struct {
	conn *Connection
	data interface{}
}

//------------------------------------------------------------------------------
type group struct {
	rwMutex sync.RWMutex
	id      string
	id2Conn map[string]*Connection
}

//------------------------------------------------------------------------------
type IWebSocket interface {
}

//------------------------------------------------------------------------------
type WebSocket struct {
	rwMutex        sync.RWMutex
	id2Conn        map[string]*Connection
	groups         map[string]*group
	users          map[string]*userInfo
	event          IEvent
	responseHeader http.Header
	bufSize        uint
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
func (o *WebSocket) Init() {
	o.id2Conn = make(map[string]*Connection)
	o.groups = make(map[string]*group)
}

//------------------------------------------------------------------------------
func (o *WebSocket) initConnection(conn *websocket.Conn, clientId string, event IEvent) *Connection {
	if event == nil {
		event = &Event{}
	}

	wsConn := &Connection{
		id:        clientId,
		wsConn:    conn,
		readChan:  make(chan *WsMessage, 1000),
		writeChan: make(chan *WsMessage, 1000),
		closeChan: make(chan byte, 1),
	}

	go wsConn.writeLoop()
	go wsConn.readLoop()

	return wsConn
}

//------------------------------------------------------------------------------
func (o *WebSocket) Upgrade(w http.ResponseWriter, r *http.Request) {
	upgrade.CheckOrigin(r)
	conn, err := upgrade.Upgrade(w, r, o.responseHeader)
	if err != nil {
		return
	}
	id := uuid.NewV4()
	clientId := id.String()
	wsConn := o.initConnection(conn, clientId, o.event)
	o.rwMutex.Lock()
	o.id2Conn[id.String()] = wsConn
	o.rwMutex.Unlock()
	fmt.Println(o.id2Conn)
	for {
		data, err := wsConn.readMessage()
		if err != nil {
			goto CLOSE
		}

		fmt.Println(data)
		o.SendToAll(data)
		//err = wsConn.writeMessage(data)
		//if err != nil {
		//	goto CLOSE
		//}
	}
CLOSE:
	wsConn.Close()
	//o.clientConnections.Store(id.String(), wsConn)

}

//------------------------------------------------------------------------------
func (o *WebSocket) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if o.event != nil {
		o.Upgrade(w, r)
	}
}

//------------------------------------------------------------------------------
func (o *WebSocket) BindEvent(event IEvent) {
	o.event = event
}

//------------------------------------------------------------------------------
func (o *WebSocket) SendToAll(data *WsMessage) {
	o.rwMutex.RLock()
	conns := o.id2Conn
	o.rwMutex.RUnlock()
	for _, conn := range conns {
		err := conn.writeMessage(data)
		if err != nil {
			// todo 打印日志
			fmt.Println(err)
		}
	}
}

//------------------------------------------------------------------------------
func (o *WebSocket) CloseClient(clientId string) error {
	o.rwMutex.RLock()
	wsConn, ok := o.id2Conn[clientId]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("client not found")
	}

	wsConn.Close()

	o.rwMutex.Lock()
	delete(o.id2Conn, clientId)
	o.rwMutex.Unlock()

	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) SendToClient(clientId string, data *WsMessage) error {
	o.rwMutex.RLock()
	wsConn, ok := o.id2Conn[clientId]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("client not found")
	}

	return wsConn.writeMessage(data)
}

//------------------------------------------------------------------------------
func (o *WebSocket) IsOnline(clientId string) bool {
	o.rwMutex.RLock()
	_, ok := o.id2Conn[clientId]
	o.rwMutex.RUnlock()
	return ok
}

//------------------------------------------------------------------------------
func (o *WebSocket) BindUid(uid, clientId string) error {
	o.rwMutex.Lock()
	defer o.rwMutex.Unlock()

	_, ok := o.users[uid]
	if ok {
		return fmt.Errorf("uid is bind")
	}

	conn, ok := o.id2Conn[clientId]
	if !ok {
		return fmt.Errorf("client not fount")
	}

	conn.uid = uid
	o.users[uid] = &userInfo{conn: conn}
	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) UnBindUid(uid string) {
	o.rwMutex.Lock()
	delete(o.users, uid)
	o.rwMutex.Unlock()
}

//------------------------------------------------------------------------------
func (o *WebSocket) IsUidOnline(uid string) bool {
	o.rwMutex.RLock()
	_, ok := o.users[uid]
	o.rwMutex.RUnlock()
	return ok
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetClientIdByUid(uid string) (string, error) {
	o.rwMutex.RLock()
	userInfo, ok := o.users[uid]
	o.rwMutex.RUnlock()
	if !ok {
		return "", fmt.Errorf("uid not found")
	}

	return userInfo.conn.id, nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetUidByClientId(clientId string) string {
	o.rwMutex.RLock()
	coon, ok := o.id2Conn[clientId]
	o.rwMutex.RUnlock()
	if !ok {
		return ""
	}

	return coon.uid
}

//------------------------------------------------------------------------------
func (o *WebSocket) SendToUid(uid string, data *WsMessage) error {
	o.rwMutex.RLock()
	userInfo, ok := o.users[uid]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("uid not found")
	}

	err := userInfo.conn.writeMessage(data)
	if err != nil {
		return err
	}
	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) JoinGroup(groupId, clientId string) error {
	// 获取连接
	o.rwMutex.RLock()
	wsConn, ok := o.id2Conn[clientId]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("client not found")
	}

	// 若用户已加入组,则返回nil表示成功
	if _, ok = wsConn.groups[groupId]; ok {
		return nil
	}

	// 获取组
	o.rwMutex.RLock()
	groupMrg, ok := o.groups[groupId]
	o.rwMutex.RUnlock()
	if !ok {
		// 若组不存在,则先添加组
		groupMrg = &group{}
		o.groups[groupId] = groupMrg
	}

	// 加入组
	groupMrg.rwMutex.Lock()
	groupMrg.id2Conn[clientId] = wsConn
	groupMrg.rwMutex.Unlock()

	// 增加用户组纪录
	wsConn.groups[groupId] = Nil{}
	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) LeaveGroup(clientId, groupId string) error {
	// 获取连接
	o.rwMutex.RLock()
	wsConn, ok := o.id2Conn[clientId]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("client not found")
	}

	// 获取组
	o.rwMutex.RLock()
	groupMrg, ok := o.groups[groupId]
	o.rwMutex.RUnlock()
	if !ok {
		// 若组不存在,则返回nil表示成功
		return nil
	}

	// 离开组
	groupMrg.rwMutex.Lock()
	delete(groupMrg.id2Conn, clientId)
	groupMrg.rwMutex.Unlock()

	// 剔除用户组纪录
	delete(wsConn.groups, groupId)
	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) UnGroup(groupId string) {
	// 解散组
	o.rwMutex.Lock()
	delete(o.groups, groupId)
	o.rwMutex.Unlock()
}

//------------------------------------------------------------------------------
func (o *WebSocket) SendToGroup(data *WsMessage, groupId string) error {
	// 获取组
	o.rwMutex.RLock()
	groupMrg, ok := o.groups[groupId]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("group not found")
	}

	for _, conn := range groupMrg.id2Conn {
		if err := conn.writeMessage(data); err != nil {
			fmt.Println(err)
		}
	}

	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetClientIdCountByGroup(groupId string) int {
	// 获取组
	o.rwMutex.RLock()
	groupMrg, ok := o.groups[groupId]
	o.rwMutex.RUnlock()
	if !ok {
		return 0
	}

	return len(groupMrg.id2Conn)
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetClientSessionByGroup(event IEvent) {

}

//------------------------------------------------------------------------------
func (o *WebSocket) GetAllClientCount() int {
	o.rwMutex.RLock()
	defer o.rwMutex.RUnlock()

	return len(o.id2Conn)
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetAllClientSessions(event IEvent) {

}

//------------------------------------------------------------------------------
func (o *WebSocket) GetAllClientIdList(event IEvent) {

}

//------------------------------------------------------------------------------
func (o *WebSocket) SetSession(uid string, data interface{}) error {
	o.rwMutex.RLock()
	userInfo, ok := o.users[uid]
	o.rwMutex.RUnlock()
	if !ok {
		return fmt.Errorf("user not found")
	}

	userInfo.data = data
	return nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) UpdateSession(event IEvent) {

}

//------------------------------------------------------------------------------
func (o *WebSocket) GetSession(uid string) (interface{}, error) {
	o.rwMutex.RLock()
	userInfo, ok := o.users[uid]
	o.rwMutex.RUnlock()
	if !ok {
		return nil, fmt.Errorf("user not found")
	}

	return userInfo.data, nil
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetUidCountByGroup(event IEvent) int {
	// todo
	return 0
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetAllUidCount() int {
	o.rwMutex.RLock()
	count := len(o.users)
	o.rwMutex.RUnlock()
	return count
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetAllUidList() (uidList []string) {
	o.rwMutex.RLock()
	users := o.users
	o.rwMutex.RUnlock()

	var uids []string
	for uid := range users {
		uids = append(uids, uid)
	}

	return uids
}

//------------------------------------------------------------------------------
func (o *WebSocket) GetAllGroupIdList() []string {
	o.rwMutex.RLock()
	groups := o.groups
	o.rwMutex.RUnlock()

	var groupIds []string
	for groupId := range groups {
		groupIds = append(groupIds, groupId)
	}

	return groupIds
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
